home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / CPUID3.ZIP / CPUID3.ASM next >
Assembly Source File  |  1994-01-04  |  18KB  |  543 lines

  1. ;       Filename:       cpuid32.msm
  2. ;
  3. ;       This program has been developed by Intel Corporation.  You have
  4. ;       Intel's permission to incorporate this source code into your
  5. ;       product royalty free.
  6. ;
  7. ;       Intel specifically disclaims all warranties, express or implied,
  8. ;       and all liability, including consequential and other indirect
  9. ;       damages, for the use of this code, including liability for
  10. ;       infringement of any proprietary rights.  Intel does not assume
  11. ;       any responsibility for any errors which may appear in this code
  12. ;       nor any responsibility to update it.
  13. ;
  14. ;       This program contains three parts:
  15. ;       Part 1: Identifies CPU type in the variable cpu_type:
  16. ;               0=8086 processor
  17. ;               2=Intel 286 processor
  18. ;               3=Intel386(TM) processor
  19. ;               4=Intel486(TM) processor
  20. ;               5=Pentium(TM) processor
  21. ;
  22. ;       Part 2: Identifies FPU type in the variable fpu_type:
  23. ;               0=FPU not present
  24. ;               1=FPU present
  25. ;               2=287 present (only if cpu_type=3)
  26. ;               3=387 present (only if cpu_type=3)
  27. ;
  28. ;       Part 3: Prints out the appropriate message.  This part can
  29. ;               be removed if this program is not used in a DOS-based
  30. ;               system.  Portions affected are at the end of the
  31. ;               data segment and the print procedure in the code
  32. ;               segment.
  33. ;
  34. ;       REVISION HISTORY:
  35. ;       Date:  4/93
  36. ;       1. Replaced the .486 with .186 to avoid generation of 0FH type long
  37. ;          conditional branches (such as the branch to end_get_cpuid at
  38. ;          the end of the 8086/8088 test, near code address 003D).  These
  39. ;          are not executable on the 8086/8088 and 80286, so the code falls
  40. ;          into some strange place and hangs the system.  The .186 also allows
  41. ;          multi-bit shifts to unpack CPUID info.  Using .186 requires 
  42. ;          that all 32-bit operand prefix (66H) be inserted by hand.  
  43. ;          This is done with a macro (OPND32).
  44. ;       2. Avoid all usage of 32-bit operands until it is clear that the
  45. ;          CPU is at least an 80386.  The use of the Exx registers caused 
  46. ;          the generation of the 66H prefix, which are not executed 
  47. ;          correctly on the 8086/8088 and 80286.  
  48. ;       3. Eliminated all the register save/restore and added comment 
  49. ;          that all registers are used by the functions.
  50. ;       4. Do the stack alignment just before messing with the AC bit in
  51. ;          EFLAGS, otherwise the stack may not be properly aligned.  Also
  52. ;          restore the AC bit immediately, so it does not stay set.
  53. ;       5. Changed the FPU detection to set one flag fpu_type (instead of
  54. ;          the previous two flags: fpu_present and infinity).  An fpu_type
  55. ;          of zero indicates no floating point unit is present, an fpu_type
  56. ;          of 2 indicates an 80287 is present, an fpu_type of 3 indicates
  57. ;          an 80387 is present.
  58. ;
  59. ;       If this code is assembled with MASM with no options specified, it
  60. ;       runs correctly on an 8086/8088, 80286, 80386, 80486, and 
  61. ;       Pentium(tm) processor.
  62. ;
  63.  
  64.  
  65.     TITLE   CPUID
  66.     DOSSEG
  67.     .model  small
  68.     .stack  100h
  69.     .186
  70.  
  71. OPND32 MACRO op_code, op_erand
  72.     db      66h     ; Force 32-bit operand size
  73.   IFNB <op_code>
  74.     db      op_code
  75.     IFNB <op_erand>
  76.     dd      op_erand; 32-bit immediate value
  77.     ENDIF
  78.   ENDIF
  79. ENDM
  80.  
  81. CPUID MACRO
  82.     db      0fh     ; Hardcoded opcode for CPUID instruction
  83.     db      0a2h
  84. ENDM
  85.  
  86. TRUE            equ     1
  87. FAMILY_MASK     equ     0f00h
  88. FAMILY_SHIFT    equ     8
  89. MODEL_MASK      equ     0f0h
  90. MODEL_SHIFT     equ     4
  91. STEPPING_MASK   equ     0fh
  92. FPU_FLAG        equ     1h
  93. MCE_FLAG        equ     80h
  94. CMPXCHG8B_FLAG  equ     100h
  95.  
  96.     .data
  97. fp_status       dw      ?
  98. vendor_id       db      12 dup (?)
  99. cpu_type        db      ?
  100. modell           db      ?
  101. stepping        db      ?
  102. id_flag         db      0
  103. fpu_type        db      0
  104. intel_proc      db      0
  105. feature_flags   dw      2 dup (0)
  106. ;
  107. ; remove the remaining data declarations if not using the DOS-based
  108. ; print procedure
  109. ;
  110. id_msg          db      "This system has a$"
  111. fp_8087         db      " and an 8087 math coprocessor$"
  112. fp_80287        db      " and an 80287 math coprocessor$"
  113. fp_80387        db      " and an 80387 math coprocessor$"
  114. c8086           db      "n 8086/8088 processor$"
  115. c286            db      "n 80286 processor$"
  116. c386            db      "n 80386 processor$"
  117. c486            db      "n 80486 DX processor or 80487 SX math coprocessor$"
  118. c486nfp         db      "n 80486 SX processor$"
  119. Intel486_msg    db      13,10,"This system contains a Genuine Intel486(TM) processor",13,10,"$"
  120. Pentium_msg     db      13,10,"This system contains a Genuine Intel Pentium(TM)  processor",13,10,"$"
  121. modelmsg        db      "Model:            $"
  122. steppingmsg     db      "Stepping:         $"
  123. familymsg       db      13,10,"Processor Family: $"
  124. period          db      ".",13,10,"$"
  125. dataCR          db      ?,13,10,"$"
  126. intel_id        db      "GenuineIntel"
  127. fpu_msg         db      13,10,"This processor contains a FPU",13,10,"$"
  128. mce_msg         db      "This processor supports the Machine Check Exception",13,10,"$"
  129. cmp_msg         db      "This processor supports the CMPXCHG8B instruction",13,10,"$"
  130. not_intel       db      "t least an 80486 processor.",13,10
  131.         db      "It does not contain a Genuine Intel part and as a result,",13,10
  132.         db      "the CPUID detection information cannot be determined at this time.",13,10,"$"
  133.  
  134. ;
  135. ;       The purpose of this code is to identify the processor and
  136. ;       coprocessor that is currently in the system.  The program first
  137. ;       determines the processor id.  When that is accomplished,
  138. ;       the program then determines whether a coprocessor
  139. ;       exists in the system.  If a coprocessor or integrated
  140. ;       coprocessor exists, the program identifies
  141. ;       the coprocessor id.  The program then prints out
  142. ;       the CPU and floating point presence and type.
  143. ;
  144.     .code
  145. start:  mov     ax, @data
  146.     mov     ds, ax          ; set segment register
  147.     mov     es, ax          ; set segment register
  148.     pushf                   ; save for restoration at end
  149.     call    get_cpuid
  150.     call    get_fpuid
  151.     call    print
  152.     popf
  153.     mov     ax, 4c00h       ; terminate program
  154.     int     21h
  155.  
  156.  
  157. get_cpuid proc
  158. ;
  159. ;       This procedure determines the type of CPU in a system
  160. ;       and sets the cpu_type variable with the appropriate
  161. ;       value.
  162. ;       All registers are used by this procedure, none are preserved.
  163.  
  164. ;       Intel 8086 CPU check
  165. ;       Bits 12-15 of the FLAGS register are always set on the
  166. ;       8086 processor.
  167. ;
  168. check_8086:
  169.     pushf                   ; push original FLAGS
  170.     pop     ax              ; get original FLAGS
  171.     mov     cx, ax          ; save original FLAGS
  172.     and     ax, 0fffh       ; clear bits 12-15 in FLAGS
  173.     push    ax              ; save new FLAGS value on stack
  174.     popf                    ; replace current FLAGS value
  175.     pushf                   ; get new FLAGS
  176.     pop     ax              ; store new FLAGS in AX
  177.     and     ax, 0f000h      ; if bits 12-15 are set, then CPU
  178.     cmp     ax, 0f000h      ;   is an 8086/8088
  179.     mov     cpu_type, 0     ; turn on 8086/8088 flag
  180.     jne     check_80286     ; jump if CPU is not 8086/8088
  181.     jmp     end_get_cpuid
  182. ;       Intel 286 CPU check
  183. ;       Bits 12-15 of the FLAGS register are always clear on the
  184. ;       Intel 286 processor in real-address mode.
  185. ;
  186. check_80286:
  187.     or      cx, 0f000h      ; try to set bits 12-15
  188.     push    cx              ; save new FLAGS value on stack
  189.     popf                    ; replace current FLAGS value
  190.     pushf                   ; get new FLAGS
  191.     pop     ax              ; store new FLAGS in AX
  192.     and     ax, 0f000h      ; if bits 12-15 clear, CPU=80286
  193.     mov     cpu_type, 2     ; turn on 80286 flag
  194.     jnz     check_80386     ; if no bits set, CPU is 80286
  195.     jmp     end_get_cpuid
  196. ;       Intel386 CPU check
  197. ;       The AC bit, bit #18, is a new bit introduced in the EFLAGS
  198. ;       register on the Intel486 DX CPU to generate alignment faults.
  199. ;       This bit cannot be set on the Intel386 CPU.
  200. ;
  201. check_80386:
  202. ;       It is now safe to use 32-bit opcode/operands
  203.     mov     bx, sp          ; save current stack pointer to align
  204.     and     sp, not 3       ; align stack to avoid AC fault
  205.     OPND32
  206.     pushf                   ; push original EFLAGS
  207.     OPND32
  208.     pop     ax              ; get original EFLAGS
  209.     OPND32
  210.     mov     cx, ax          ; save original EFLAGS
  211.     OPND32  35h, 40000h     ; flip AC bit in EFLAGS
  212.     OPND32
  213.     push    ax              ; save new EFLAGS value on stack
  214.     OPND32
  215.     popf                    ; replace current EFLAGS value
  216.     OPND32
  217.     pushf                   ; get new EFLAGS
  218.     OPND32
  219.     pop     ax              ; store new EFLAGS in EAX
  220.     OPND32
  221.     xor     ax, cx          ; can't toggle AC bit, CPU=80386
  222.     mov     cpu_type, 3     ; turn on 80386 CPU flag
  223.     mov     sp, bx          ; restore original stack pointer
  224.     jz      end_get_cpuid   ; jump if 80386 CPU
  225.     and     sp, not 3       ; align stack to avoid AC fault
  226.     OPND32
  227.     push    cx
  228.     OPND32
  229.     popf                    ; restore AC bit in EFLAGS first
  230.     mov     sp, bx          ; restore original stack pointer
  231.  
  232. ;       Intel486 DX CPU, Intel487 SX NDP, and Intel486 SX CPU check
  233. ;       Checking for ability to set/clear ID flag (Bit 21) in EFLAGS
  234. ;       which indicates the presence of a processor
  235. ;       with the ability to use the CPUID instruction.
  236. ;
  237. check_80486:
  238.     mov     cpu_type, 4     ; turn on 80486 CPU flag
  239.     OPND32
  240.     mov     ax, cx          ; get original EFLAGS
  241.     OPND32  35h, 200000h    ; flip ID bit in EFLAGS
  242.     OPND32
  243.     push    ax              ; save new EFLAGS value on stack
  244.     OPND32
  245.     popf                    ; replace current EFLAGS value
  246.     OPND32
  247.     pushf                   ; get new EFLAGS
  248.     OPND32
  249.     pop     ax              ; store new EFLAGS in EAX
  250.     OPND32
  251.     xor     ax, cx          ; can't toggle ID bit,
  252.     je      end_get_cpuid   ;   CPU=80486
  253.  
  254. ;       Execute CPUID instruction to determine vendor, family,
  255. ;       model and stepping.
  256. ;
  257. check_vendor:
  258.     mov     id_flag, 1              ; set flag indicating use of CPUID inst.
  259.     OPND32
  260.     xor     ax, ax                  ; set up input for CPUID instruction
  261.     CPUID                           ; macro for CPUID instruction
  262.     OPND32
  263.     mov     word ptr vendor_id, bx  ; setup to test for vendor id
  264.     OPND32
  265.     mov     word ptr vendor_id[+4], dx
  266.     OPND32
  267.     mov     word ptr vendor_id[+8], cx
  268.     mov     si, offset vendor_id
  269.     mov     di, offset intel_id
  270.     mov     cx, length intel_id
  271. compare:
  272.     repe    cmpsb                   ; compare vendor id to "GenuineIntel"
  273.     or      cx, cx
  274.     jnz     end_get_cpuid           ; if not zero, not an Intel CPU,
  275.  
  276. intel_processor:
  277.     mov     intel_proc, 1
  278.  
  279. cpuid_data:
  280.     OPND32
  281.     cmp     ax, 1                   ; make sure 1 is a valid input
  282.                     ; value for CPUID
  283.     jl      end_get_cpuid           ; if not, jump to end
  284.     OPND32
  285.     xor     ax, ax                  ; otherwise, use as input to CPUID
  286.     OPND32
  287.     inc     ax                      ; and get stepping, model and family
  288.     CPUID
  289.     mov     stepping, al
  290.     and     stepping, STEPPING_MASK ; isolate stepping info
  291.  
  292.     and     al, MODEL_MASK          ; isolate model info
  293.     shr     al, MODEL_SHIFT
  294.     mov     modell, al
  295.  
  296.     and     ax, FAMILY_MASK         ; mask everything but family
  297.     shr     ax, FAMILY_SHIFT
  298.     mov     cpu_type, al            ; set cpu_type with family
  299.  
  300.     OPND32
  301.     mov     feature_flags, dx       ; save feature flag data
  302.  
  303. end_get_cpuid:
  304.     ret
  305. get_cpuid endp
  306.  
  307.  
  308. ;******************************************************************
  309.  
  310. get_fpuid proc
  311. ;
  312. ;       This procedure determines the type of FPU in a system
  313. ;       and sets the fpu_type variable with the appropriate
  314. ;       value.
  315. ;       All registers are used by this procedure, none are preserved.
  316.  
  317. ;       Coprocessor check
  318. ;       The algorithm is to determine whether the floating-point
  319. ;       status and control words can be written to.  If not, no
  320. ;       coprocessor exists.  If the status and control words can be
  321. ;       written to, the correct coprocessor is then determined
  322. ;       depending on the processor id.  The Intel386 CPU can
  323. ;       work with either an Intel287 NDP or an Intel387 NDP.
  324. ;       The infinity of the coprocessor must be
  325. ;       checked to determine the correct coprocessor id.
  326.  
  327.     fninit                  ; reset FP status word
  328.     mov     fp_status, 5a5ah; initialize temp word to
  329.                 ; non-zero value
  330.     fnstsw  fp_status       ; save FP status word
  331.     mov     ax, fp_status   ; check FP status word
  332.     cmp     al, 0           ; see if correct status with
  333.                 ; written
  334.     mov     fpu_type, 0     ; no fpu present
  335.     jne     end_get_fpuid
  336.  
  337. check_control_word:
  338.     fnstcw  fp_status       ; save FP control word
  339.     mov     ax, fp_status   ; check FP control word
  340.     and     ax, 103fh       ; see if selected parts
  341.                 ; looks OK
  342.     cmp     ax, 3fh         ; check that 1's & 0's
  343.                 ; correctly read
  344.     mov     fpu_type, 0
  345.     jne     end_get_fpuid
  346.     mov     fpu_type, 1
  347.  
  348. ;
  349. ;   80287/80387 check for the Intel386 CPU
  350. ;
  351. check_infinity:
  352.     cmp     cpu_type, 3
  353.     jne     end_get_fpuid
  354.     fld1                    ; must use default control from FNINIT
  355.     fldz                    ; form infinity
  356.     fdiv                    ; 8087 and Intel287 NDP say +inf = -inf
  357.     fld     st              ; form negative infinity
  358.     fchs                    ; Intel387 NDP says +inf <> -inf
  359.     fcompp                  ; see if they are the same and remove them
  360.     fstsw   fp_status       ; look at status from FCOMPP
  361.     mov     ax, fp_status
  362.     mov     fpu_type, 2     ; store Intel287 NDP for fpu type
  363.     sahf                    ; see if infinities matched
  364.     jz      end_get_fpuid   ; jump if 8087 or Intel287 is present
  365.     mov     fpu_type, 3     ; store Intel387 NDP for fpu type
  366. end_get_fpuid:
  367.     ret
  368. get_fpuid endp
  369.  
  370.  
  371. ;*********************************************************************
  372.  
  373. print proc
  374. ;
  375. ;       This procedure prints the appropriate cpuid string and
  376. ;       numeric processor presence status.  If the CPUID instruction
  377. ;       was supported, this procedure prints out cpuid info.
  378. ;       All registers are used by this procedure, none are preserved.
  379.  
  380.     cmp     id_flag, 1              ; if set to 1, cpu supports
  381.                     ;   CPUID instruction
  382.                     ; print detailed CPUID information
  383.     jne     cont
  384.     jmp     print_cpuid_data
  385.  
  386. cont:   mov     dx, offset id_msg       ; print initial message
  387.     mov     ah, 9h
  388.     int     21h
  389.  
  390. print_86:
  391.     cmp     cpu_type, 0
  392.     jne     print_286
  393.     mov     dx, offset c8086
  394.     mov     ah, 9h
  395.     int     21h
  396.     cmp     fpu_type, 0
  397.     jne     cont2
  398.     jmp     end_print
  399. cont2:  mov     dx, offset fp_8087
  400.     mov     ah, 9h
  401.     int     21h
  402.     jmp     end_print
  403.  
  404. print_286:
  405.     cmp     cpu_type, 2
  406.     jne     print_386
  407.     mov     dx, offset c286
  408.     mov     ah, 9h
  409.     int     21h
  410.     cmp     fpu_type, 0
  411.     jne     cont3
  412.     jmp     end_print
  413. cont3:  mov     dx, offset fp_80287
  414.     mov     ah, 9h
  415.     int     21h
  416.     jmp     end_print
  417.  
  418. print_386:
  419.     cmp     cpu_type, 3
  420.     jne     print_486
  421.     mov     dx, offset c386
  422.     mov     ah, 9h
  423.     int     21h
  424.     cmp     fpu_type, 0
  425.     jne     cont4
  426.     jmp     end_print
  427. cont4:  cmp     fpu_type, 2
  428.     jne     print_387
  429.     mov     dx, offset fp_80287
  430.     mov     ah, 9h
  431.     int     21h
  432.     jmp     end_print
  433.  
  434. print_387:
  435.     mov     dx, offset fp_80387
  436.     mov     ah, 9h
  437.     int     21h
  438.     jmp     end_print
  439.  
  440. print_486:
  441.     cmp     fpu_type, 0
  442.     je      print_Intel486sx
  443.     mov     dx, offset c486
  444.     mov     ah, 9h
  445.     int     21h
  446.     jmp     end_print
  447.  
  448. print_Intel486sx:
  449.     mov     dx, offset c486nfp
  450.     mov     ah, 9h
  451.     int     21h
  452.     jmp     end_print
  453.  
  454. print_cpuid_data:
  455.  
  456. cmp_vendor:
  457.     cmp     intel_proc, 1
  458.     je      cont5
  459.     jmp     not_GenuineIntel
  460. cont5:  cmp     cpu_type, 4                     ; if cpu_type=4, print
  461.                         ; Intel486 CPU message
  462.     jne     check_Pentium
  463.     mov     dx, offset Intel486_msg
  464.     mov     ah, 9h
  465.     int     21h
  466.     jmp     print_family
  467.  
  468. check_Pentium:
  469.     cmp     cpu_type, 5                     ; if cpu_type=5, print
  470.     jne     print_features                  ; Pentium processor message
  471.     mov     dx, offset Pentium_msg
  472.     mov     ah, 9h
  473.     int     21h
  474.  
  475. print_family:
  476.     mov     dx, offset familymsg            ; print family msg
  477.     mov     ah, 9h
  478.     int     21h
  479.     mov     al, cpu_type
  480.     mov     byte ptr dataCR, al
  481.     add     byte ptr dataCR, 30h            ; convert to ASCII
  482.     mov     dx, offset dataCR               ; print family info
  483.     mov     ah, 9h
  484.     int     21h
  485.  
  486. print_model:
  487.     mov     dx, offset modelmsg             ; print model msg
  488.     mov     ah, 9h
  489.     int     21h
  490.     mov     al, modell
  491.     mov     byte ptr dataCR, al
  492.     add     byte ptr dataCR, 30h            ; convert to ASCII
  493.     mov     dx, offset dataCR               ; print model info
  494.     mov     ah, 9h
  495.     int     21h
  496.  
  497. print_stepping:
  498.     mov     dx, offset steppingmsg          ; print stepping msg
  499.     mov     ah, 9h
  500.     int     21h
  501.     mov     al, stepping
  502.     mov     byte ptr dataCR, al
  503.     add     byte ptr dataCR, 30h            ; convert to ASCII
  504.     mov     dx, offset dataCR               ; print stepping info
  505.     mov     ah, 9h
  506.     int     21h
  507.  
  508. print_features:
  509.     mov     ax, feature_flags
  510.     and     ax, FPU_FLAG                    ; check for FPU
  511.     jz      check_MCE
  512.     mov     dx, offset fpu_msg
  513.     mov     ah, 9h
  514.     int     21h
  515.  
  516. check_MCE:
  517.     mov     ax, feature_flags
  518.     and     ax, MCE_FLAG                    ; check for MCE
  519.     jz      check_CMPXCHG8B
  520.     mov     dx, offset mce_msg
  521.     mov     ah, 9h
  522.     int     21h
  523.  
  524. check_CMPXCHG8B:
  525.     mov     ax, feature_flags
  526.     and     ax, CMPXCHG8B_FLAG              ; check for CMPXCHG8B
  527.     jz      end_print
  528.     mov     dx, offset cmp_msg
  529.     mov     ah, 9h
  530.     int     21h
  531.     jmp     end_print
  532.  
  533. not_GenuineIntel:
  534.     mov     dx, offset not_Intel
  535.     mov     ah, 9h
  536.     int     21h
  537.  
  538. end_print:
  539.     ret
  540. print endp
  541.  
  542.     end     start
  543.